/**
 * Copyright (c) 2012-2016, www.tinygroup.org (luo_guo@icloud.com).
 *
 *  Licensed under the GPL, Version 3.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *       http://www.gnu.org/licenses/gpl.html
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.tinygroup.jdbctemplatedslsession;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyAccessorFactory;
import org.tinygroup.jdbctemplatedslsession.editor.AllowNullNumberEditor;
import org.tinygroup.jdbctemplatedslsession.keygenerator.AppKeyGenerator;
import org.tinygroup.jdbctemplatedslsession.keygenerator.DatabaseKeyGenerator;
import org.tinygroup.logger.LogLevel;
import org.tinygroup.logger.Logger;
import org.tinygroup.logger.LoggerFactory;
import org.tinygroup.tinysqldsl.Insert;
import org.tinygroup.tinysqldsl.KeyGenerator;
import org.tinygroup.tinysqldsl.base.Column;
import org.tinygroup.tinysqldsl.base.InsertContext;
import org.tinygroup.tinysqldsl.insert.InsertBody;

import java.beans.PropertyDescriptor;
import java.util.List;

/**
 * 对象映射
 *
 * @author renhui
 */
public class ObjectMapper<T> extends BaseMappedClass<T> {
    private static final Logger LOGGER = LoggerFactory
            .getLogger(ObjectMapper.class);

    private SimpleDslSession dslSession;

    public ObjectMapper() {
        super();
    }

    public ObjectMapper(Class<T> mappedClass) {
        super(mappedClass);
    }

    public void setDslSession(SimpleDslSession dslSession) {
        this.dslSession = dslSession;
    }

    public T assemble(boolean autoGeneratedKeys, TableMetaData tableMetaData,
                      Insert insert) {
        Object mappedObject = BeanUtils.instantiateClass(mappedClass);
        BeanWrapper bw = PropertyAccessorFactory
                .forBeanPropertyAccess(mappedObject);
        initBeanWrapper(bw);
        InsertContext newContext = insert.getContext().copyContext();
        List<ColumnMetaData> columnMetaDatas = tableMetaData
                .getColumnMetaDatas();
        boolean needExecute = true;// 是否需要执行数据库操作，如果是数据库生成自增长主键方式，由于在生成主键刚才中已经将记录插入到数据库中，那么就不需要再次执行插入sql
        boolean hasAcquiredPrimary = false;
        for (ColumnMetaData columnMetaData : columnMetaDatas) {
            String column = columnMetaData.getParameterName().toLowerCase();
            PropertyDescriptor pd = (PropertyDescriptor) this.mappedFields
                    .get(column);
            if (pd != null) {
                try {
                    Object value = null;
                    if (isPrimaryColumn(column, tableMetaData)
                            && !newContext.existParam(column)) {// 主键字段，并且主键值不存在
                        if (hasAcquiredPrimary) {
                            //已经通过主键生成机制获取过主键值则返回,只支持单个主键未设置值方式。
                            continue;
                        }
                        value = getPrimaryValue(autoGeneratedKeys, column,
                                tableMetaData, insert);
                        if (autoGeneratedKeys) {// 数据库自增长方式，needExecute置为false
                            needExecute = false;
                        } else {// 数据库生成方式已经在keygenerator中执行数据库插入操作,不需要在插入上下文增加主键值
                            Column primaryColumn = new Column(
                                    newContext.getTable(), column);
                            newContext.addValues(primaryColumn.value(value));
                        }
                        hasAcquiredPrimary = true;
                    } else {
                        value = getColumnValue(column, tableMetaData,
                                newContext);
                    }
                    bw.setPropertyValue(pd.getName(), value);
                } catch (BeansException ex) {
                    LOGGER.logMessage(LogLevel.DEBUG,
                            "Unable to map column {0} to property {1}", ex,
                            column, pd.getName());
                }

            }
        }
        if (needExecute) {// 执行数据库插入操作
            InsertBody insertBody = newContext.createInsert();
            String sql = insertBody.toString();
            dslSession.getJdbcTemplate().update(sql,
                    newContext.getParamValues());
        }
        return (T) bw.getWrappedInstance();
    }

    /**
     * 获取非主键字段的值
     *
     * @param tableMetaData
     * @param insert
     * @return
     */
    private Object getColumnValue(String columnName,
                                  TableMetaData tableMetaData, InsertContext insertContext) {
        Object value = insertContext.getParamValue(columnName);
        if (value == null) {
            value = tableMetaData.getDefaultValue(columnName);
        }
        return value;
    }

    /**
     * 判断是不是主键字段
     *
     * @param column
     * @param tableMetaData
     * @return
     */
    private boolean isPrimaryColumn(String columnName,
                                    TableMetaData tableMetaData) {
        return tableMetaData.isKeyName(columnName);
    }

    private Object getPrimaryValue(boolean autoGeneratedKeys, String keyName,
                                   TableMetaData tableMetaData, Insert insert) {
        InsertContext context = insert.getContext();
        KeyGenerator keyGenerator = null;
        if (autoGeneratedKeys) {
            keyGenerator = new DatabaseKeyGenerator(
                    dslSession.getJdbcTemplate(), insert, tableMetaData);
        } else {
            keyGenerator = context.getTable().getGeneratorMap().get(keyName);
            if (keyGenerator == null) {// 没有重写table对象的getGeneratorMap方法，那么从配置中加载
                keyGenerator = dslSession.getConfiguration().getKeyGenerator(
                        context.getTableName(), keyName);
            }
            if (keyGenerator == null) {// 如果没有配置默认的keygenerator，则使用AppKeyGenerator
                keyGenerator = new AppKeyGenerator(dslSession.getIncrementer());
            }
        }
        return keyGenerator.generate(context);
    }

    /**
     * Initialize the given BeanWrapper to be used for row mapping. To be called
     * for each row.
     * <p>
     * The default implementation is empty. Can be overridden in subclasses.
     *
     * @param bw the BeanWrapper to initialize
     */
    protected void initBeanWrapper(BeanWrapper bw) {
        bw.registerCustomEditor(byte.class, new AllowNullNumberEditor(
                Byte.class, true));
        bw.registerCustomEditor(short.class, new AllowNullNumberEditor(
                Short.class, true));
        bw.registerCustomEditor(int.class, new AllowNullNumberEditor(
                Integer.class, true));
        bw.registerCustomEditor(long.class, new AllowNullNumberEditor(
                Long.class, true));
        bw.registerCustomEditor(float.class, new AllowNullNumberEditor(
                Float.class, true));
        bw.registerCustomEditor(double.class, new AllowNullNumberEditor(
                Double.class, true));
    }

}
